﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace FerrScript
{
    public class FScript
    {
        #region Fields
        ScriptCore          core;
        ScriptCore          defaultCore;
        CompiledFunction[]  functions;
        CompilerInstruction instructions;

        string scriptText;
        #endregion

        #region Properties
        /// <summary>
        /// Allows you to manipulate the variables in the active core.        
        /// </summary>
        public ScriptCore Core
        {
            get { return core; }
        }
        #endregion

        #region Constructor
        internal FScript( string aScript, CompilerInstruction aIns, ScriptVariable[] aLocalVariables, CompiledFunction[] aFunctions)
        {
            scriptText         = aScript;
            instructions       = aIns;
            functions          = aFunctions;

            core        = new ScriptCore(aLocalVariables);
            defaultCore = new ScriptCore(aLocalVariables);
        }
        #endregion

        #region Methods
        /// <summary>
        /// Runs the script this object represents, and returns the last value evaluated
        /// </summary>
        /// <returns>The last value evaluated from the script</returns>
        public ScriptValue Run()
        {
            ScriptValue eval = new ScriptValue(0);
            eval = instructions.Evaluate();
            return eval;
        }

        /// <summary>
        /// Searches the script for a function by the given name, and calls it. Returns a 0 if not found.
        /// </summary>
        /// <param name="aFunctionName">Name of the function in the script</param>
        /// <returns>Returns the value that the function itself returns, or an integer with the value of 0</returns>
        public ScriptValue CallFunction(string aFunctionName)
        {
            return CallFunction(aFunctionName, null);
        }
        /// <summary>
        /// Searches the script for a function by the given name, and calls it, providing a list of arguments. Returns a 0 if not found.
        /// </summary>
        /// <param name="aFunctionName">Name of the function in the script</param>
        /// <param name="aArguments">a list of objects to be passed to the function as arguments</param>
        /// <returns>Returns the value that the function itself returns, or an integer with the value of 0</returns>
        public ScriptValue CallFunction(string aFunctionName, object[] aArguments)
        {
            ScriptValue ret = new ScriptValue(0);

            for (int i = 0; i < functions.Length; i++)
            {
                if (functions[i].name == aFunctionName)
                {
                    // check arguments
                    if (aArguments != null && aArguments.Length != functions[i].arguments.Length)
                        FerrScriptCompiler.Inst.ThrowException("Invalid number of arguments provided to function "+aFunctionName+"!");

                    // set up the function's arguments
                    if (aArguments != null)
                    {
                        for (int a = 0; a < aArguments.Length; a++)
                            functions[i].arguments[a].value = new ScriptValue(aArguments[a]);
                    }

                    // execute the function
                    if (functions[i].rootInstruction.instructions != null)
                    {
                        for (int s = functions[i].arguments.Length; s < functions[i].rootInstruction.instructions.Length; s++)
                            ret = functions[i].rootInstruction.instructions[s].Evaluate();
                    }
                    break;
                }
            }
            return ret;
        }
        #endregion

        #region Core Methods
        /// <summary>
        /// Gets the default core for the script, with all variables at their default
        /// values.
        /// </summary>
        /// <returns>A copy of the default script core</returns>
        public ScriptCore GetDefaultCore()
        {
            return new ScriptCore(defaultCore);
        }
        /// <summary>
        /// Copies the active core into the core provided
        /// </summary>
        /// <param name="aCore">A core created from this script instance's default core</param>
        public void GetCore(ref ScriptCore aCore)
        {
            aCore.Copy(core);
        }
        /// <summary>
        /// This copies an existing core into this script instance.
        /// </summary>
        /// <param name="aCore">The new core values</param>
        public void SetCore(ScriptCore aCore)
        {
            core.Copy(aCore);
        }
        #endregion
    }
}
